<template>
  <div class="city-container">
    <div class="message">
      <div>通过对默认shader的自定义，实现动态初始化建筑动画</div>
    </div>
    <div id="container" ref="container"></div>
  </div>
</template>

<script>
import { InitThree } from "../../utils/init-three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";

import { DirectionalLight, Color, Vector3, Clock } from "three";

export default {
  name: "base-city",
  data() {
    return {
      initThree: null,
      scene: null,
      draco: null,
      dracoLoader: null,
      clock: new Clock(),
      isStart: false,
      startTime: { value: 0 },
      time: {
        value: 0,
      },
    };
  },
  mounted() {
    this.initCity();
  },
  methods: {
    initCity() {
      this.initThree = new InitThree(this.$refs.container);
      this.scene = this.initThree.scene;
      this.addLight();
      this.loadingModel();
    },

    addLight() {
      const directionalLight = new DirectionalLight(0xffffff, 2);
      directionalLight.position.set(10, 10, 10);
      this.scene.add(directionalLight);
      // const helper = new DirectionalLightHelper(directionalLight, 5);
      // this.scene.add(helper);
    },

    loadingModel() {
      const loader = new GLTFLoader();
      this.dracoLoader = new DRACOLoader();
      this.dracoLoader.setDecoderPath("model/gltf/draco/");
      loader.setDRACOLoader(this.dracoLoader);
      loader.load(
        "model/gltf/model.gltf",
        (gltf) => {
          const group = gltf.scene;
          group.traverse((child) => {
            if (child.name == "city") {
              this.setCityMaterial(child);
            }
            if (child.name == "floor") {
              child.material.color.setStyle("#333333");
            }
          });
          this.scene.add(group);
          this.isStart = true;
          this.animate();
        },
        (xhr) => {
          console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
        },
        (error) => {
          console.log(error);
        }
      );
    },

    animate() {
      const dt = this.clock.getDelta();
      this.cityAnimate(dt);
      this.initThree.render();
      requestAnimationFrame(this.animate.bind(this));
    },

    cityAnimate(dt) {
      if (dt > 1) return false;
      this.time.value += dt;
      if (this.isStart) {
        this.startTime.value += dt * 0.6;
        if (this.startTime.value >= 1) {
          this.startTime.value = 1;
          this.isStart = false;
        }
      }
    },

    setCityMaterial(child) {
      const { material, geometry } = child;
      geometry.computeBoundingBox();
      geometry.computeBoundingSphere();
      const { center, radius } = geometry.boundingSphere;
      const { max, min } = geometry.boundingBox;

      const size = new Vector3(max.x - min.x, max.y - min.y, max.z - min.z);

      material.transparent = true;
      material.color.setStyle("#367aab");
      material.onBeforeCompile = (shader) => {
        // 建筑特效
        this.addCityEffects(shader, size, radius, center);
      };
    },

    addCityEffects(shader, size, radius, center) {
      shader.uniforms.uStartTime = this.startTime;
      shader.uniforms.uSize = { value: size };
      shader.uniforms.uTopColor = {
        value: new Color("#fff700"),
      };

      // 流光相关数据
      shader.uniforms.time = this.time;
      shader.uniforms.uFlow = {
        value: new Vector3(
          1, // 0 1开关
          0.4, // 范围
          3 // 速度
        ),
      };
      shader.uniforms.uFlowColor = {
        value: new Color("#ffffff"),
      };

      // 扩散波相关数据
      shader.uniforms.uDiffusion = {
        value: new Vector3(
          1, // 0 1开关
          2, // 范围
          6 // 速度
        ),
      };
      shader.uniforms.uRadius = {
        value: radius,
      };
      shader.uniforms.uCenter = {
        value: center,
      };
      shader.uniforms.uColor = {
        value: new Color("#04f8fd"),
      };

      // 修改片元着色器
      const fragment = `
                    float distanceTo(vec2 src, vec2 dst) {
                        float dx = src.x - dst.x;
                        float dy = src.y - dst.y;
                        float dv = dx * dx + dy * dy;
                        return sqrt(dv);
                    }

                    varying vec3 vPosition;

                    uniform float uStartTime; 
                    uniform vec3 uSize;
                    uniform vec3 uTopColor;

                    // 流光所需变量
                    uniform vec3 uFlow;
                    uniform float time;
                    uniform vec3 uFlowColor;

                    // 扩散波所需变量
                    uniform vec3 uDiffusion; 
                    uniform float uRadius; 
                    uniform vec3 uCenter;
                    uniform vec3 uColor;

                    void main() {
                `;
      const fragmentColor = `
                    vec3 distColor = outgoingLight;
                    float dstOpacity = diffuseColor.a;

                    float indexMix = vPosition.y / (uSize.y * 0.7);
                    distColor = mix(distColor, uTopColor, indexMix);

                    // 流光效果
                    if (uFlow.x > 0.0) {
                        // 扩散速度
                        // mod()取余数
                        float dTime = mod(time * uFlow.z, uSize.y); 
                        // 流动范围
                        float topY = vPosition.y + uFlow.y;
                        if (dTime > vPosition.y && dTime < topY) {
                            // 颜色渐变 
                            float dIndex = sin((topY - dTime) / uFlow.y * PI);

                            distColor = mix(distColor, uFlowColor,  dIndex); 
                        }
                    }

                    // 扩散波
                    vec2 position2D = vec2(vPosition.x, vPosition.z);
                    if (uDiffusion.x > 0.5) {
                        // 扩散速度
                        float dTime = mod(time * uDiffusion.z, uRadius * 2.0);
                        // 当前的离中心点距离
                        float uLen = distanceTo(position2D, vec2(uCenter.x, uCenter.z));

                        // 扩散范围
                        if (uLen < dTime && dTime <(uLen+ uDiffusion.y)) {
                            // 颜色渐变
                            float dIndex = sin((dTime - uLen) / uDiffusion.y * PI);
                            distColor = mix(uColor, distColor, 1.0 - dIndex);
                        }
                    }

                    // gl_FragColor = vec4(distColor, dstOpacity * uStartTime); //透明度
                    gl_FragColor = vec4(distColor, dstOpacity);

                `;
      shader.fragmentShader = shader.fragmentShader.replace(
        "void main() {",
        fragment
      );
      shader.fragmentShader = shader.fragmentShader.replace(
        "#include <output_fragment>",
        fragmentColor
      );

      /**
       * 对顶点着色器进行修改
       */
      const vertex = `
                    varying vec3 vPosition;
                    uniform float uStartTime;
                    void main() {
                            vPosition = position;
                    `;
      const vertexPosition = `
                    vec3 transformed = vec3(position.x, position.y* uStartTime, position.z );
                `;

      shader.vertexShader = shader.vertexShader.replace(
        "void main() {",
        vertex
      );
      shader.vertexShader = shader.vertexShader.replace(
        "#include <begin_vertex>",
        vertexPosition
      );
    },
  },
  beforeDestroy() {
    this.initThree.destroy();
    this.dracoLoader.dispose();
    this.dracoLoader = null;
  },
};
</script>

<style lang="scss" scoped>
.city-container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}
.message {
  height: 50px;
  width: 100%;
}
#container {
  flex: 1;
  width: 100%;
}
</style>
